package main

import (
	"fmt"
	"unsafe"
)

/*
go语言不支持面向对象编程 -- 使用结构体来实现类似其他语言中的class
可以在结构体中定义多个字段（属性），为结构体实现方法，实例化等
结构体 -- struct
描述一系列具有相同类型或不同类型组成的数据集合
比如说：记录一个学生的信息
id 学号
name 名字
age 年龄
class 班级

mysql -- 关系型数据库 -- 一行一个结构体

定义结构体：
type struct_name struct{
	field_name type
	field_name type
	....
	field_name type
}

方法的实现
go语言只需要在普通函数前面加一个接收者(receiver 写在函数名前面的括号里)
func (s ReceiverType) funcName([parameters]) [results]{
	方法内容
}

*/

//type Student struct {
//	id int
//	name string
//	age int
//	class string
//	school string
//}
//
//func (s Student) change_school(){
//	fmt.Printf("方法里面s的地址为：%p\n",&s)
//	s.school = "湖南农业大学"
//	fmt.Println(s.school)
//}
//
//func (s *Student) change_school2(){
//	s.school = "湖南大学"
//	fmt.Println(s.school)
//}
//
//func main()  {
	//xiaoxiangwu := &Student{
	//	id:1,
	//	name:"xiaoxiangwu",
	//	class:"mjh",
	//}
	//fmt.Println((*xiaoxiangwu).id) //通过指针类型去取值，前面要加*
	//fmt.Println(xiaoxiangwu.id) //语法糖 简化开发 在指针类型结构体中，前面的* 可以省略
	//
	//xiaoxiangwu.age = 20
	//xiaoxiangwu.class = "manjianghong"
	//fmt.Println(xiaoxiangwu)
	//
	////使用new实例化，开辟一片内存空间，创建一个对象实例，然后返回这个内存空间的地址（指证类型）
	//xxw := new(Student)
	//xxw.id = 2
	//xxw.name = "xiao"
	//fmt.Println(xxw)
//	new 和 make 都是申请内存空间
//	make 是生成slice、map、channel等数据类型的函数，返回对象本身
//	new 申请内存空间返回的是地址，接收的数据类型是指针

//	创建一个实例，值类型
//	ldh := Student{
//		id : 3,
//		name : "liudehua",
//	}
//	fmt.Println(ldh)
	//s1 := Student{
	//	id : 1 ,
	//	name : "xxw",
	//}
//	实例化的时候不需要每个字段都赋值，没有显性赋值的变量将被授予默认值
//	fmt.Printf("s1的地址为:%p\n", &s1)
//	s1.change_school2()
//	fmt.Println("s1的school为:",s1.school)
//}
/*
使用值类型作为接收者 它传递进来的是一份完全的拷贝
s跟外部调用的s1是没有联系的，在方法里面修改s，不会影响外部的s1

使用指针类型作为接收者 它传递进来的是一份引用
修改s，会影响外部s

结构体匿名字段 以类型的名字作为字段的名字
type struct_name struct{
	string
	int
	...
}

匿名结构体  没有名字的结构体  不需要type关键字去定义，定义结构体的时候就实例化对象
变量名 := struct{
	//结构体的成员属性
}
{
	//初始化属性成员
}
 */
//匿名字段结构体
//type Stu struct {
//	string
//	int
//}
//
//func main(){
//	s1 := Stu{string:"abc",int:123}
//	fmt.Println(s1)
//
//	//匿名结构体
//	s2 := struct{
//		name string
//		age int
//	}{
//		name: "sc",
//		age: 5,
//	}
//	//%v 只会输出结构体的值	 %+v -- 输出结构体字段和值的映射关系
//	fmt.Printf("%v\n",s2)
//	fmt.Printf("%+v",s2)
//}

type TestStu struct {
	a bool
	b string
	c int16
}

type TestStu2 struct {
	a string
	b bool
	c int16
}

func main()  {
	t2 := TestStu2{}
	fmt.Println("t1 size:",unsafe.Sizeof(t2))
}

/*
内存对齐:为了减少cpu对内存的访问次数，提高cpu读取内存数据的效率，而做的内存上的填充
如果内存不对齐，访问相同的数据需要更多的访问内存次数，效率就低下了

数据类型的大小和对齐系数
unsafe包 Sizeof函数 -- 看占用大小
		Alightof函数 -- 看对齐系数

内存对齐规则：
规则一：结构体第一个成员变量偏移量为0，后面的成员变量的偏移量等于成员变量大小和成员对齐系数两者中较小的那个值的最小整数倍
	如果不满足规则，编译器就会在前面填充为0的字节空间
规则二：结构体本身也需要内存对齐，其大小等于各成员变量占用内存最大的和编译器默认对齐系数两者中较小的那个值的最小整数倍

程序员可以通过调整成员变量的顺序减少结构体占用大小，节省内存
一个大原则：将占用大小大的变量放前面
 */



